<?php
/**
 * Created by PhpStorm.
 * User: legendnan
 * Date: 2017-03-04
 * Time: 14:08
 * QQ  : 524881622
 */
namespace think;

class Loader
{
    /*
     * static 声明类属性或方法为静态，就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问（但静态方法可以）。 由于静态方法不需要通过对象即可调用，所以伪变量 $this 在静态方法中不可用。静态属性不可以由对象通过 -> 操作符来访问。 就像其它所有的 PHP 静态变量一样，静态属性只能被初始化为文字或常量，不能使用表达式。所以可以把静态属性初始化为整数或数组，但不能初始化为另一个变量或函数返回值，也不能指向一个对象。
     *
     * */
    protected static $instance = [];
    //类名映射
    protected static $map = [];
    //命名空间别名
    protected static $namespaceAlias = [];

    //PSR-4
    private static $prefixLengthsPsr4 = [];
    private static $prefixDirsPsr4    = [];
    private static $fallbackDirsPsr4  = [];

    //PSR-0
    private static $prefixesPsr0     = [];
    private static $fallbackDirsPsr0 = [];

    //自动加载的文件
    private static $autoloadFiles = [];
    /*
     * 自动加载
     * dirname 给出一个包含有指向一个文件的全路径的字符串，本函数返回去掉文件名后的目录名。
     * class_exists 如果由 class_name 所指的类已经定义，此函数返回 TRUE，否则返回 FALSE。
     * \\转义\
     * $class 如果实例化 hhhkkk\kkk\Obj;就会获取到hhhkkk\kkk\Obj,如果没有命名空间 $class获取的是类名 有命名空间就会把命名空间加前面
比如 new Goog  $class会获取到 Good
     * */
    public static function autoload($class)
    {
        if (!empty(self::$namespaceAlias)) {
            $namespace = dirname($class);
            if (isset(self::$namespaceAlias[$namespace])) {
                $original = self::$namespaceAlias[$namespace].'\\'.basename($class);
                if (class_exists($original)) {
                    return class_alias($original, $class, false);
                }
            }
        }


        if ($file = self::findFile($class)){
            //Win环境雅阁区分大小写
            if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
                return false;
            }
            __include_file($file);
            return true;
        }
    }

    /*
     * 查找文件
     * @param $class
     * return bool
     * strtr 转换指定字符
     * strpos 查找字母首次出现的位置
     * substr 返回字符串的子串
     * is_file 判断一个文件名是否为一个正常文件
     * */
    private static function findFile($class)
    {
        if (!empty(self::$map[$class])) {
            //类库映射？
            return self::$map[$class];
        }
        //查找 PSR-4
        $logicalPathPsr4 = strtr($class, '\\', DS) . EXT;

        $first = $class[0];
        if (isset(self::$prefixLengthsPsr4[$first])) {
            foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length){
                if (0 === strpos($class, $prefix)) {
                    foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
                        if (is_file($file = $dir.DS.substr($logicalPathPsr4, $length))) {
                            return $file;
                        }
                    }
                }
            }
        }

        /*
         * 查找PSR-4 fallback dirs
         * */
        foreach (self::$fallbackDirsPsr4 as $dir) {
            if (is_file($file = $dir.DS.$logicalPathPsr4)){
                return $file;
            }
        }

        /*
         * 查找PSR-0
         * */
        if (false !== $pos = strrpos($class, '\\')){
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                .strtr(substr($logicalPathPsr4, $pos+1), '_',DS);
        }else{
            $logicalPathPsr0 = strtr($class, '_', DS).EXT;
        }

        if (isset(self::$prefixesPsr0[$first])) {
            foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (is_file($file = $dir . DS . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }

        //查找PSR-0 fallback dirs
        foreach (self::$fallbackDirsPsr0 as $dir) {
            if (is_file($file = $dir . DS . $logicalPathPsr0)) {
                return $file;
            }
        }

        return self::$map[$class] = false;

    }

    /*
     * 作用范围隔离
     * 关于spl_autoload_register于自动加载看http://www.kuitao8.com/20131231/1879.shtml。http://www.cnblogs.com/zyf-zhaoyafei/p/4542457.html后面这个文章比较透彻，前面的适合理解。无参的spl_autoload_register()会默认加载spl_autoload()函数，该函数功能有限，只能在inlcude_path中搜索指定扩展名的类库。有参的spl_autoload_register()默认不再加载spl_autoload()函数。可以通过spl_autoload_functions()查看当前自动加载hashtable中的函数，该函数返回一个数组。https://segmentfault问题.com/q/1010000002510575关于spl_autoload_register参数为。
     *
     * */

    public static function register($autoload = '')
    {
        spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
        //注册命名空间
        self::addNamespace([
            'think' => LIB_PATH . 'think' . DS,
            'behavior' => LIB_PATH . 'behavior' . DS,
            'traits' => LIB_PATH . 'traits' . DS,
        ]);
        // 加载类库映射文件
        if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
            self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
        }
        //自动加载支持
        if (is_dir(VENDOR_PATH . 'composer')) {
            self::registerComposerLoader();
        }
        //自动加载extend目录
        self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);

    }



    //注册命名空间
    /*
     * rtrim 出去右边的空格
     * */
    public static function addNamespace($namespace, $path = '')
    {
        if (is_array($namespace)) {
            foreach ($namespace as $prefix => $paths) {
                self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
            }
        } else {
            self::addPsr4($namespace . '\\', rtrim($path, DS), true);
        }
    }

    // 添加Psr4空间
    /*
     * array_merge合并数组
     * strlen获取字符串长度
     * */
    private static function addPsr4($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                self::$fallbackDirsPsr4 = array_merge(
                    (array)$paths,
                    self::$fallbackDirsPsr4
                );
            } else {
                self::$fallbackDirsPsr4 = array_merge(
                    self::$fallbackDirsPsr4,
                    (array)$paths
                );
            }
        } elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            self::$prefixDirsPsr4[$prefix] = (array)$paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            self::$prefixDirsPsr4[$prefix] = array_merge(
                (array)$paths,
                self::$prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            self::$prefixDirsPsr4[$prefix] = array_merge(
                self::$prefixDirsPsr4[$prefix],
                (array)$paths
            );
        }
    }

    //注册命名空间
    public static function addClassMap($class, $map = '')
    {
        if (is_array($class)) {
            self::$map = array_merge(self::$map, $class);
        } else {
            self::$map[$class] = $map;
        }
    }
    /*
     * 注册自动加载机制
     * spl_autoload_register
     * */
    // 注册composer自动加载
    private static function registerComposerLoader()
    {
        if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
            $map = require VENDOR_PATH . 'composer/autoload_namespaces.php';
            foreach ($map as $namespace => $path) {
                self::addPsr0($namespace, $path);
            }
        }

        if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) {
            $map = require VENDOR_PATH . 'composer/autoload_psr4.php';
            foreach ($map as $namespace => $path) {
                self::addPsr4($namespace, $path);
            }
        }

        if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) {
            $classMap = require VENDOR_PATH . 'composer/autoload_classmap.php';
            if ($classMap) {
                self::addClassMap($classMap);
            }
        }

        if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
            $includeFiles = require VENDOR_PATH . 'composer/autoload_files.php';
            foreach ($includeFiles as $fileIdentifier => $file) {
                if (empty(self::$autoloadFiles[$fileIdentifier])) {
                    __require_file($file);
                    self::$autoloadFiles[$fileIdentifier] = true;
                }
            }
        }
    }

    // 添加Ps0空间
    private static function addPsr0($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            if ($prepend) {
                self::$fallbackDirsPsr0 = array_merge(
                    (array)$paths,
                    self::$fallbackDirsPsr0
                );
            } else {
                self::$fallbackDirsPsr0 = array_merge(
                    self::$fallbackDirsPsr0,
                    (array)$paths
                );
            }

            return;
        }

        $first = $prefix[0];
        if (!isset(self::$prefixesPsr0[$first][$prefix])) {
            self::$prefixesPsr0[$first][$prefix] = (array)$paths;

            return;
        }
        if ($prepend) {
            self::$prefixesPsr0[$first][$prefix] = array_merge(
                (array)$paths,
                self::$prefixesPsr0[$first][$prefix]
            );
        } else {
            self::$prefixesPsr0[$first][$prefix] = array_merge(
                self::$prefixesPsr0[$first][$prefix],
                (array)$paths
            );
        }
    }

}

function __include_file($file)
{
    return include $file;
}

function __require_file($file)
{
    return require $file;
}